package wator.model.strategies;

import java.util.Map;

import wator.model.Agent;
import wator.model.IntegerVector;
import wator.model.MovementStrategy;
import wator.model.Neighborhood;
import wator.model.Neighborhood.Neighbor;

/**
 * This movement strategy allows to combine random movements with the inertia
 * of the individual. The resulting movement vector is a weighted sum of the
 * direction vector and a vector generated from a random movement strategy.
 * 
 * @see RandomMovementStrategy
 */
public class InertiaRandomMovementStrategy implements MovementStrategy {

	RandomMovementStrategy randomStrategy;
	boolean onSpeed;
	double maxVelocity = 3;
	double inertiaWeight = 60;
	
	/**
	 * Sole constructor. Initializes the underlying random movement strategy
	 * and other initial variables.
	 * 
	 * @param onSpeed defines if the moving strategy is in "on speed" mode, i.e.
	 * 		always trying to maximize its own velocity
	 */
	public InertiaRandomMovementStrategy(boolean onSpeed) {
		super();
		this.onSpeed = onSpeed;
		randomStrategy = new RandomMovementStrategy();
	}
	
	/* (non-Javadoc)
	 * @see wator.model.MovementStrategy#apply(wator.model.Agent, wator.model.Neighborhood)
	 */
	@Override
	public IntegerVector apply(Agent agent, Neighborhood neighborhood) {
		boolean valid;
		IntegerVector dir = agent.getDirectionVector().mul(inertiaWeight);
		for (int i = 0; i < 100; i++) {
			IntegerVector v = randomStrategy.getRandomVector()
					.mul(100f - inertiaWeight).add(dir).mul(1f / 100f);
			final double length = v.length();
			if (length < 1) { // avoid null vector
				continue;
			}
			if (onSpeed || length > maxVelocity) {
				v = v.mul(maxVelocity / length);
			}
			valid = true;
			for (Neighbor n : neighborhood) {
				if (n.getDistanceVector().equals(v)) {
					valid = false;
					break;
				}
			}
			if (valid) {
				return v;
			}
		}
		return randomStrategy.apply(agent, neighborhood);
	}

	/* (non-Javadoc)
	 * @see wator.model.MovementStrategy#setParameter(java.lang.String, int)
	 */
	@Override
	public void setParameter(String key, int value) {
		if (key.equals("Inertia Weight")) {
			inertiaWeight = value;
		} else if (key.equals("Max. Velocity")) {
			maxVelocity = value;
		}
	}

	/* (non-Javadoc)
	 * @see wator.model.MovementStrategy#getParameters()
	 */
	@Override
	public Map<String, Integer> getParameters() {
		Map<String, Integer> params = randomStrategy.getParameters();
		params.put("Inertia Weight", (int) inertiaWeight);
		params.put("Max. Velocity", (int) maxVelocity);
		return params;
	}

	/* (non-Javadoc)
	 * @see wator.model.MovementStrategy#getName()
	 */
	@Override
	public String getName() {
		return "Inertia-Random" + (onSpeed ? " On Speed" : "");
	}

}
